#include "MarsPrecompile.h"

#include "MarsShader.h"
#include "MarsRenderer.h"
#include "MarsConstantMgr.h"

using namespace Graphics;
using namespace Core;

Shader::Shader( ShaderType eType, const char* pcszFileName, const char* pcszEntry, const char* pcszShaderModel )
	: m_eShaderType( eType )
	, m_strFileName( pcszFileName )
	, m_strEntry( pcszEntry )
	, m_strShaderModel( pcszShaderModel )
	, m_pBuffer( NULL )
{
	std::string str = m_strFileName + m_strEntry;
	m_iHash = StrCRC::CRC( str );
}

void Shader::Release()
{
	{
		ParameterMapItr it = m_mapParameters.begin();
		ParameterMapItr end = m_mapParameters.end();

		for ( ; it != end; ++it )
		{
			SAFE_DELETE( it->second );
		}

		m_mapParameters.clear();
	}

	{
		ShaderSlotMapItr it = m_mapShaderSlots.begin();
		ShaderSlotMapItr end = m_mapShaderSlots.end();

		for ( ; it != end; ++it )
		{
			SAFE_DELETE_ARRAY( it->second.Buffer );

			if ( it->second.VBuffer )
				it->second.VBuffer->SetUsing( false );
		}
	}

	SAFE_RELEASE( m_pBuffer );
}

void Shader::SetParameter( ShaderSemantic semantic, void* pBuffer )
{
	uint32 iNameHash = ShaderParameter::GetParameterName( semantic );

	ParameterMapItr it = m_mapParameters.find( iNameHash );
	if ( it == m_mapParameters.end() )
		return;

	ShaderParameter& parameter = * ( it->second );

	MARS_ASSERT( m_mapShaderSlots.find( parameter.GetSlot() ) != m_mapShaderSlots.end() );
	MARS_MEMCPY( m_mapShaderSlots[ parameter.GetSlot() ].Buffer + parameter.GetOffset(), pBuffer, parameter.GetSize() );

	m_mapShaderSlots[ parameter.GetSlot() ].Dirty = true;
}

void Shader::UploadParameters()
{
	ShaderSlotMapItr it = m_mapShaderSlots.begin();
	ShaderSlotMapItr end = m_mapShaderSlots.end();
	
	ID3D11DeviceContext* pContext = Renderer::Instance().GetDeviceContext();

	for ( ; it != end; ++it )
	{
		ShaderSlot& slot = it->second;

		if ( !slot.VBuffer )
		{
			slot.VBuffer = ConstantMgr::Instance().GetConstantBuffer( slot.Size );
			slot.VBuffer->SetUsing( true );
		}

		if ( slot.Dirty )
		{
			pContext->UpdateSubresource( slot.VBuffer->GetNativeBuffer(), 0, 0, slot.Buffer, 0, 0 );
			slot.Dirty = false;
		}
	}

	SetCBufferToContext();
}

void Shader::GenerateUserDataBuffer()
{
	ShaderSlotMapItr it = m_mapShaderSlots.begin();
	ShaderSlotMapItr end = m_mapShaderSlots.end();

	for ( ; it != end; ++it )
	{
		ShaderSlot& slot = it->second;

		SAFE_DELETE( slot.Buffer );

		slot.Buffer = new uint8[ slot.Size ];
		MARS_MEMSET( slot.Buffer, 0, slot.Size );
	}
}